package dbms;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;

import javax.xml.parsers.ParserConfigurationException;

import expression.ExpressionException;

public abstract class DataBase {

	private String dbName;
	protected FileMgr fm = new FileMgr();
	protected HashMap<String, Table> openTableMap = new HashMap<String, Table>();
	protected LinkedList<String> openTableNames = new LinkedList<String>();
	private int maxNumOfOpenTables = 5;

	//

	private ArrayList<String> getTableNames() {
		return FileMgr.getTableNames(dbName);
	}

	// DATABASE

	private ArrayList<String> getDataBaseNames() {
		return FileMgr.getDataBaseNames();
	}

	public void setCurrentDatabaseName(String database) {
		this.dbName = database;
	}

	public String getCurrentDatabaseName() {
		return dbName;
	}

	// FILES

	private Table open(String tbl) throws DBException, ParserConfigurationException, IOException {
		Table out = null;
		// if the table is already open
		if (openTableNames.contains(tbl))
			out = openTableMap.get(tbl);
		// if not load it
		else {
			out = load(tbl);
			openTableMap.put(tbl, out);
			openTableNames.addFirst(tbl);
			while (openTableNames.size() > maxNumOfOpenTables) {
				close(openTableNames.getLast());
			}
		}
		return out;
	}

	private void close(String tbl) throws ParserConfigurationException, DBException, IOException {
		// if the table is already open
		if (openTableNames.contains(tbl)) {
			save(tbl);
			openTableNames.remove(tbl);
			openTableMap.remove(tbl);
		}
	}

	public void closeAll() throws ParserConfigurationException, DBException, IOException {
		for (String tbl : openTableNames)
			close(tbl);
	}

	private void save(String tbl) throws ParserConfigurationException, DBException, IOException {
		fm.saveTable(openTableMap.get(tbl), dbName);
	}

	private Table load(String tbl) throws ParserConfigurationException, DBException, IOException {
		return fm.loadTable(tbl, dbName);
	}

	// SELECT

	public TableIterator select(String table, String[] cols, String condition) throws DBException, ExpressionException, ParserConfigurationException, IOException {
		return TableIterator.selectIterator(open(table), cols, condition);

	}

	// INSERT

	public Table createTable(String name, String[] cols, String[] types) throws DBException {
		if (openTableMap.containsKey(name))
			throw new DBException(DBException.TBL_DUPLICATE);
		Table t = new Table(name);
		openTableMap.put(t.getName(), t);
		if (cols != null && types != null)
			for (int i = 0; i < types.length; i++)
				t.addColumn(cols[i], types[i]);
		return t;
	}

	public void insert(String table, String[] in) throws DBException, ParserConfigurationException, IOException {
		Table t = open(table);
		t.addRow((String[]) in);
	}

	// UPDATE

	public TableIterator update(String table, String condition, String[] setColumns, String[] values) throws DBException, ExpressionException,
		ParserConfigurationException, IOException {
		return TableIterator.updateIterator(open(table), condition, setColumns, values);
	}

	// DELETE *

	public TableIterator delete(String table, String condition) throws DBException, ExpressionException, ParserConfigurationException, IOException {
		return TableIterator.deleteIterator(open(table), condition);
	}

	//

	public static void main(String[] args) {
		DataBase db = new DataBase() {};
		db.setCurrentDatabaseName("db");
		System.out.println(db.getDataBaseNames());
		System.out.println(db.getTableNames() + "\n");
		//
		Table t = null;
		try {
			t = db.open("School");
			System.out.println(t);
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			db.save("School");
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println();
		// select
		try {
			TableIterator ti = db.select("School", new String[]
			{ "ID", "Name" }, "ID > 4 & Name = 'Bob'");
			System.out.println(ti.getOutput());
			System.out.println(ti.getUpdateCount());
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println();
		// delete
//		try {
//			TableIterator ti = db.delete("School", "ID > 4 & Name = 'Sam'");
//			System.out.println(t);
//			System.out.println(ti.getUpdateCount());
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//		System.out.println();
		// update
		try {
			TableIterator ti = db.update("School", "ID > 4 & Name = 'Sam'", new String[]
			{ "Name" }, new String[]
			{ "'Ne' + ''" });
			System.out.println(t);
			System.out.println(ti.getUpdateCount());
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println();
		// insert Name Hobby Class ID
		try {
			db.insert("School", new String[]
			{ "Asd", "Basd", "Dasd", "12" });
		} catch (Exception e) {
			e.printStackTrace();
		}
		// table after filter
		System.out.println();
		System.out.println(t);
	}

}